/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Insets;
import java.awt.Shape;
import java.awt.Point;
import java.awt.Font;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Iterator;
import javax.swing.text.*;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.UIManager;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.Action;
import javax.swing.border.Border;
/**
* Text UI implementation
*
* @author Miloslav Metelka
* @version 1.00
*/
public class BaseTextUI extends TextUI
implements ViewFactory, PropertyChangeListener, DocumentListener {
/** Get rid of mantisa problems */
private static final int MAX_SPAN = Integer.MAX_VALUE - 512;
/** Minimum component width */
private static final int MIN_WIDTH = 300;
/** Minimum component height */
private static final int MIN_HEIGHT = 200;
/** Editor component */
private JTextComponent component;
/** Extended UI */
private ExtUI extUI;
/** Instance of the <tt>GetFocusedComponentAction</tt> */
private static final GetFocusedComponentAction gfcAction
= new GetFocusedComponentAction();
/** Root view of view hierarchy */
private RootView rootView;
public BaseTextUI() {
rootView = new RootView();
}
static JTextComponent getFocusedComponent() {
return gfcAction.getFocusedComponent2();
}
/** Called when the model of component is changed */
protected void modelChanged(BaseDocument oldDoc, BaseDocument newDoc) {
if (oldDoc != null) {
oldDoc.removeDocumentListener(this);
}
if (newDoc != null) {
newDoc.addDocumentListener(this);
ViewFactory f = rootView.getViewFactory();
BaseKit kit = (BaseKit)getEditorKit(component);
component.removeAll();
Element elem = newDoc.getDefaultRootElement();
View v = f.create(elem);
rootView.setView(v);
rootView.updateMainHeight(); // compute actual height of views
component.revalidate();
// Execute actions related to document installaction into the component
Settings.KitAndValue[] kv = Settings.getKitAndValueArray(kit.getClass(),
Settings.DOC_INSTALL_ACTION_NAME_LIST);
for (int i = kv.length - 1; i >= 0; i--) {
List actList = (List)kv[i].value;
actList = kit.translateActionNameList(actList); // translate names to actions
if (actList != null) {
for (Iterator iter = actList.iterator(); iter.hasNext();) {
Action a = (Action)iter.next();
a.actionPerformed(new ActionEvent(component,
ActionEvent.ACTION_PERFORMED, "")); // NOI18N
}
}
}
}
}
/** Update height of the views */
void updateHeight() {
rootView.updateMainHeight();
}
/** Installs the UI for a component. */
public void installUI(JComponent c) {
if (c instanceof JTextComponent) {
component = (JTextComponent) c; // this is associated component
getExtUI().installUI(component);
component.setOpaque(true); // opaque by default
component.setAutoscrolls(true); // autoscrolling by default
// attach to the model and component
component.addPropertyChangeListener(this);
component.addPropertyChangeListener(new UIWatcher(this.getClass()));
BaseKit kit = (BaseKit)getEditorKit(component);
// Create and attach caret
Caret caret = kit.createCaret();
component.setCaretColor(Color.black); // will be changed by settings later
component.setCaret(caret);
// assign blink rate
int br = SettingsUtil.getInteger(Utilities.getKitClass(component), Settings.CARET_BLINK_RATE,
DefaultSettings.defaultCaretBlinkRate.intValue());
caret.setBlinkRate(br);
// Create document
Document doc = component.getDocument();
if (doc instanceof BaseDocument) {
modelChanged(null, (BaseDocument)doc);
}
/** Patch for 1.3 - assigns a null UI input map into the component.
* The following block stands for the following code:
*
* SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
*
*/
try {
Class inputMapClass = Class.forName("javax.swing.InputMap"); // NOI18N
if (inputMapClass != null) {
java.lang.reflect.Method replaceUIInputMapMethod = SwingUtilities.class.getDeclaredMethod(
"replaceUIInputMap", new Class[] { JComponent.class, Integer.TYPE, inputMapClass }); // NOI18N
replaceUIInputMapMethod.invoke(null,
new Object[] { c, new Integer(JComponent.WHEN_FOCUSED), null });
}
} catch (Throwable t) {
}
Keymap km = kit.getKeymap();
component.setKeymap(km);
Registry.addComponent(component);
component.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
}
}
/** Deinstalls the UI for a component */
public void uninstallUI(JComponent c) {
component.removePropertyChangeListener(this);
component.getDocument().removeDocumentListener(this);
rootView.setView(null); // sets inner view of root view to null
component.removeAll();
component.setKeymap(null);
getExtUI().uninstallUI(component);
// Clear the extUI so it will be recreated according to the kit
// of the component for which the installUI is called
extUI = null;
}
/** Paint the UI.
*
* @param g the graphics context
* @param c the editor component
*/
public void paint(Graphics g, JComponent c) {
if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
BaseDocument doc = (BaseDocument)component.getDocument();
doc.readLock();
try {
// paint the view hierarchy
getExtUI().paint(g);
// paint the caret
Caret caret = component.getCaret();
if (caret != null) {
caret.paint(g);
}
// check virtual size change
if (getExtUI().virtualSizeUpdated) {
preferenceChanged(true, true);
}
} finally {
doc.readUnlock();
}
}
}
/** Paint either image region or classic graphics region */
void paintRegion(Graphics g) {
rootView.paint(g, null);
}
/** Gets the preferred size for the editor component. If the component
* has been given a size prior to receiving this request, it will
* set the size of the view hierarchy to reflect the size of the component
* before requesting the preferred size of the view hierarchy. This
* allows formatted views to format to the current component size before
* answering the request. Other views don't care about currently formatted
* size and give the same answer either way.
*
* @param c the editor component
* @return the size
*/
public Dimension getPreferredSize(JComponent c) {
Insets i = c.getInsets();
Insets margin = getExtUI().getTextMargin();
Dimension d = c.getSize();
BaseDocument doc = (BaseDocument)component.getDocument();
doc.readLock();
try {
// first try to change the root view size
if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
}
// now get the real preferred size
d.width = (int)Math.min(rootView.getPreferredSpan(View.X_AXIS) +
i.left + i.right + margin.left + margin.right, MAX_SPAN);
d.height = (int)Math.min(rootView.getPreferredSpan(View.Y_AXIS) +
i.top + i.bottom + margin.top + margin.bottom, MAX_SPAN);
} finally {
doc.readUnlock();
}
return d;
}
/** Gets the minimum size for the editor component.
*
* @param c the editor component
* @return the size
*/
public Dimension getMinimumSize(JComponent c) {
Insets i = c.getInsets();
Insets margin = getExtUI().getTextMargin();
Dimension d = new Dimension();
BaseDocument doc = (BaseDocument)component.getDocument();
doc.readLock();
try {
d.width = (int) rootView.getMinimumSpan(View.X_AXIS)
+ i.left + i.right + margin.left + margin.right;
d.height = (int) rootView.getMinimumSpan(View.Y_AXIS)
+ i.top + i.bottom + margin.top + margin.bottom;
} finally {
doc.readUnlock();
}
return d;
}
/** Gets the maximum size for the editor component.
*
* @param c the editor component
* @return the size
*/
public Dimension getMaximumSize(JComponent c) {
Insets i = c.getInsets();
Insets margin = getExtUI().getTextMargin();
Dimension d = new Dimension();
BaseDocument doc = (BaseDocument)component.getDocument();
doc.readLock();
try {
d.width = (int)Math.min(rootView.getMaximumSpan(View.X_AXIS)
+ i.left + i.right + margin.left + margin.right, MAX_SPAN);
d.height = (int)Math.min(rootView.getMaximumSpan(View.Y_AXIS)
+ i.top + i.bottom + margin.top + margin.bottom, MAX_SPAN);
} finally {
doc.readUnlock();
}
return d;
}
public void invalidateStartY() {
rootView.invalidateStartY();
}
/** Similair to modelToView() but without acquiring the document read lock. */
public Rectangle modelToView(JTextComponent c, int pos)
throws BadLocationException {
return (Rectangle)rootView.modelToView(pos, null, Position.Bias.Forward);
}
public Rectangle modelToView(JTextComponent c, int pos, Position.Bias bias)
throws BadLocationException {
return (Rectangle)rootView.modelToView(pos, null, bias);
}
public void modelToViewDG(int pos, Drawer.DrawGraphics dg) throws BadLocationException {
rootView.modelToViewDG(pos, dg);
}
public int getYFromPos(int pos) throws BadLocationException {
return rootView.getYFromPos(pos);
}
public int getPosFromY(int y) throws BadLocationException {
return rootView.getPosFromY(y);
}
public int getBaseX(int y) {
return rootView.getBaseX(y);
}
public int viewToModel(JTextComponent c, Point pt) {
return viewToModel(c, pt.x, pt.y);
}
public int viewToModel(JTextComponent c, int x, int y) {
return rootView.viewToModel(x, y, null, null);
}
public int viewToModel(JTextComponent c, Point pt, Position.Bias[] biasReturn) {
return rootView.viewToModel(pt.x, pt.y, null, biasReturn);
}
/** Next visually represented model location where caret can be placed.
* This version works without placing read lock on the document.
*/
public int getNextVisualPositionFrom(JTextComponent t, int pos,
Position.Bias b, int direction, Position.Bias[] biasRet)
throws BadLocationException{
return rootView.getNextVisualPositionFrom(pos, b, null, direction, biasRet);
}
public void damageRange(JTextComponent c, int p0, int p1) {
damageRange(c, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
}
/** Causes the portion of the view responsible for the
* given part of the model to be repainted.
*
* @param p0 the beginning of the range >= 0
* @param p1 the end of the range >= p0
*/
public void damageRange(JTextComponent t, int p0, int p1,
Position.Bias p0Bias, Position.Bias p1Bias) {
BaseDocument doc = (BaseDocument)component.getDocument();
doc.readLock();
try {
Rectangle r = (Rectangle)rootView.modelToView(p0, p0Bias, p1, p1Bias, null);
component.repaint(r.x, r.y, r.width, r.height);
} catch (BadLocationException e) {
} finally {
doc.readUnlock();
}
}
/** Fetches the EditorKit for the UI.
*
* @return the component capabilities
*/
public EditorKit getEditorKit(JTextComponent c) {
JEditorPane pane = (JEditorPane)component;
return pane.getEditorKit();
}
/** Fetches a root view of the view hierarchy. */
public View getRootView(JTextComponent c) {
return rootView;
}
/** Get extended UI. This is called from views to get correct extended UI. */
public ExtUI getExtUI() {
if (extUI == null) {
BaseKit kit = (BaseKit)getEditorKit(component);
extUI = kit.createExtUI();
}
return extUI;
}
/**
* This method gets called when a bound property is changed.
* We are looking for document changes on the component.
*/
public void propertyChange(PropertyChangeEvent evt) {
String propName = evt.getPropertyName();
if ("document".equals(propName)) {
BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument)
? (BaseDocument)evt.getOldValue() : null;
BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument)
? (BaseDocument)evt.getNewValue() : null;
modelChanged(oldDoc, newDoc);
}
}
/** Insert to document notification. */
public void insertUpdate(DocumentEvent evt) {
rootView.insertUpdate(evt, null, rootView.getViewFactory());
if (((BaseDocumentEvent)evt).getLFCount() > 0
|| evt.getLength() == 0
) {
if (getExtUI().updateVirtualHeight(rootView.getHeight())) {
preferenceChanged(true, true);
}
getExtUI().checkLineLimit();
if (evt.getLength() == 0) { // initial read performed !!! needed?
component.repaint(500);
}
}
}
/** Remove from document notification. */
public void removeUpdate(DocumentEvent evt) {
rootView.removeUpdate(evt, null, rootView.getViewFactory());
if (((BaseDocumentEvent)evt).getLFCount() > 0) {
if (getExtUI().updateVirtualHeight(rootView.getHeight())) {
preferenceChanged(true, true);
}
}
}
/** The change in document notification.
*
* @param e The change notification from the currently associated document.
*/
public void changedUpdate(DocumentEvent evt) {
BaseDocumentEvent bdevt = (BaseDocumentEvent)evt;
BaseDocument doc = (BaseDocument)bdevt.getDocument();
String layerName = bdevt.getDrawLayerName();
if (layerName != null) {
getExtUI().addLayer(doc.findLayer(layerName));
} else { // some other type of change, propagate to root view
rootView.changedUpdate(evt, null, rootView.getViewFactory());
}
}
/** Creates a view for an element.
*
* @param elem the element
* @return the newly created view or null
*/
public View create(Element elem) {
View v = null;
if (elem instanceof BaseElement) {
v = new LeafView(elem);
}
return v;
}
/** Creates a view for an element.
* @param elem the element
* @param p0 the starting offset >= 0
* @param p1 the ending offset >= p0
* @return the view
*/
public View create(Element elem, int p0, int p1) {
return new LeafView(elem);
}
// from JEditorPane ui
public static ComponentUI createUI(JComponent c) {
return new BaseTextUI();
}
/** Specifies that some preference has changed. */
public void preferenceChanged(boolean width, boolean height) {
getExtUI().virtualSizeUpdated = false;
component.revalidate();
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
Caret caret = component.getCaret();
if (caret instanceof BaseCaret) {
((BaseCaret)caret).dispatchUpdate();
}
}
}
);
}
/** Root view */
class RootView extends BaseView {
BaseView view;
RootView() {
super(null);
}
/** Sets the only one inner view of root view to either null or a valid view. */
void setView(View v) {
if (v instanceof BaseView) { // only BaseView instances are supported
if (view != null) {
// enable grb.col.
view.setParent(null);
}
view = (BaseView)v;
if (view != null) {
view.setParent(this);
}
}
}
/** Fetches attributes associated with view */
public AttributeSet getAttributes() {
return null;
}
/** Determines the preferred span for this view along an axis.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the span the view would like to be rendered into.
*/
public float getPreferredSpan(int axis) {
ExtUI extUI = BaseTextUI.this.getExtUI();
switch (axis) {
case X_AXIS:
return Math.max(BaseTextUI.this.getExtUI().virtualSize.width, MIN_WIDTH);
case Y_AXIS:
return Math.max(BaseTextUI.this.getExtUI().virtualSize.height, MIN_HEIGHT);
}
return 0f;
}
/** Determines minimum span along an axis */
public float getMinimumSpan(int axis) {
return getPreferredSpan(axis);
}
/** Determines maximum span along an axis */
public float getMaximumSpan(int axis) {
return Integer.MAX_VALUE;
}
/** Specifies that a preference has changed. */
public void preferenceChanged(View child, boolean width, boolean height) {
BaseTextUI.this.preferenceChanged(width, height);
}
/** Determines the desired alignment for this view along an axis. */
public float getAlignment(int axis) {
if (view != null) {
return view.getAlignment(axis);
}
return 0;
}
/** Renders the view. */
public void paint(Graphics g, Shape allocation) {
if (view != null) {
view.paint(g, allocation);
}
}
/** Sets the parent view. */
public void setParent(View parent) {
// root view has no parent
}
/** Returns the number of views in this view. */
public int getViewCount() {
return 1;
}
/** Gets the n-th view in this container. */
public View getView(int n) {
return view;
}
/** Fetches the allocation for the given child view.
* Returns the whole allocated area.
*/
public Shape getChildAllocation(int index, Shape a) {
return a;
}
/** Provides a mapping from the document model coordinate space
* to the coordinate space of the view mapped to it.
*/
public Shape modelToView(int pos, Shape a, Position.Bias b)
throws BadLocationException {
if (view != null) {
return view.modelToView(pos, a, b);
}
return null;
}
public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a)
throws BadLocationException {
if (view != null) {
return view.modelToView(p0, b0, p1, b1, a);
}
return null;
}
public void modelToViewDG(int pos, Drawer.DrawGraphics dg) throws BadLocationException {
if (view != null) {
view.modelToViewDG(pos, dg);
}
}
/** Determine next visually represented model location where caret
* can be placed.
*/
public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
int direction, Position.Bias[] biasRet) throws BadLocationException {
if (view != null) {
int nextPos = view.getNextVisualPositionFrom(pos, b, a, direction, biasRet);
if (nextPos != -1) {
pos = nextPos;
} else {
if (biasRet != null) {
biasRet[0] = b;
}
}
}
return pos;
}
/** Provides a mapping from the view coordinate space to the logical
* coordinate space of the model.
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b) {
if (view != null) {
return view.viewToModel(x, y, a, b);
}
return -1;
}
/** Get y-coord value from position */
protected int getYFromPos(int pos) throws BadLocationException {
if (view != null) {
return view.getYFromPos(pos);
}
return 0;
}
/** Get position when knowing y-coord */
protected int getPosFromY(int y) {
if (view != null) {
return view.getPosFromY(y);
}
return -1;
}
protected int getBaseX(int y) {
if (view != null) {
return view.getBaseX(y);
}
return 0;
}
/** Gives notification that something was inserted into the document
* in a location that this view is responsible for.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
if (view != null) {
view.insertUpdate(e, a, f);
}
}
/** Gives notification that something was removed from the document
* in a location that this view is responsible for.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
if (view != null) {
view.removeUpdate(e, a, f);
}
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
if (view != null) {
view.changedUpdate(e, a, f);
}
}
/** Returns the document model underlying the view. */
public Document getDocument() {
return component.getDocument();
}
/** Returns the starting offset into the model for this view. */
public int getStartOffset() {
if (view != null) {
return view.getStartOffset();
}
return getElement().getStartOffset();
}
/**
* Returns the ending offset into the model for this view.
*
* @return the ending offset
*/
public int getEndOffset() {
if (view != null) {
return view.getEndOffset();
}
return getElement().getEndOffset();
}
/** Gets the element that this view is mapped to. */
public Element getElement() {
if (view != null) {
return view.getElement();
}
return component.getDocument().getDefaultRootElement();
}
/** Breaks this view on the given axis at the given length. */
public View breakView(int axis, float len, Shape a) {
return null; // no breaking of the root view
}
/** Determines the resizability of the view along the
* given axis. A value of 0 or less is not resizable. */
public int getResizeWeight(int axis) {
if (view != null) {
return view.getResizeWeight(axis);
}
return 0;
}
/** Sets the view size. */
public void setSize(float width, float height) {
if (view != null) {
view.setSize(width, height);
}
}
/** Fetches the container hosting the view. */
public Container getContainer() {
return component;
}
/** Fetches the factory to be used for building the
* various view fragments that make up the view that
* represents the model. This is what determines
* how the model will be represented. This is implemented
* to fetch the factory provided by the associated
* EditorKit unless that is null, in which case this
* simply returns the BasicTextUI itself which allows
* subclasses to implement a simple factory directly without
* creating extra objects.
*
* @return the factory
*/
public ViewFactory getViewFactory() {
EditorKit kit = getEditorKit(component);
ViewFactory f = kit.getViewFactory();
if (f != null) {
return f;
}
return BaseTextUI.this;
}
protected int getStartY() {
return BaseTextUI.this.getExtUI().textMargin.top;
}
protected int getPaintAreas(Graphics g, int clipY, int clipHeight) {
return 0; // no paint areas
}
protected void paintAreas(Graphics g, int clipY, int clipHeight, int paintAreas) {
// no painting for the root view
}
protected int getViewStartY(BaseView view, int helperInd) {
return getStartY();
}
protected void invalidateStartY() {
if (view != null) {
view.invalidateStartY();
}
}
public int getHeight() {
if (view != null) {
return view.getHeight();
}
return 0;
}
public void updateMainHeight() {
if (view != null) {
view.updateMainHeight();
ExtUI extUI = BaseTextUI.this.getExtUI();
if (extUI.updateVirtualHeight(getHeight())) {
BaseTextUI.this.preferenceChanged(true, true);
}
}
}
}
private static class GetFocusedComponentAction extends TextAction {
private GetFocusedComponentAction() {
super("get-focused-component");
}
public void actionPerformed(ActionEvent evt) {
}
JTextComponent getFocusedComponent2() {
return super.getFocusedComponent();
}
}
/** Class that returns back BaseTextUI after its change
* by changing look-and-feel.
*/
static class UIWatcher implements PropertyChangeListener {
private Class uiClass;
UIWatcher(Class uiClass) {
this.uiClass = uiClass;
}
public void propertyChange(PropertyChangeEvent evt) {
if ("UI".equals(evt.getPropertyName()) // NOI18N
&& !(evt.getNewValue() instanceof BaseTextUI)
) {
JTextComponent c = (JTextComponent)evt.getSource();
try {
c.setUI((BaseTextUI)uiClass.newInstance());
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
}
c.removePropertyChangeListener(this);
}
}
}
};
/*
* Log
* 34 Gandalf-post-FCS1.31.1.1 4/3/00 Miloslav Metelka undo update
* 33 Gandalf-post-FCS1.31.1.0 3/8/00 Miloslav Metelka
* 32 Gandalf 1.31 1/14/00 Miloslav Metelka
* 31 Gandalf 1.30 1/13/00 Miloslav Metelka
* 30 Gandalf 1.29 12/28/99 Miloslav Metelka
* 29 Gandalf 1.28 11/14/99 Miloslav Metelka
* 28 Gandalf 1.27 11/8/99 Miloslav Metelka
* 27 Gandalf 1.26 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 26 Gandalf 1.25 10/10/99 Miloslav Metelka
* 25 Gandalf 1.24 9/16/99 Miloslav Metelka
* 24 Gandalf 1.23 9/10/99 Miloslav Metelka
* 23 Gandalf 1.22 8/17/99 Miloslav Metelka
* 22 Gandalf 1.21 7/26/99 Miloslav Metelka
* 21 Gandalf 1.20 7/21/99 Miloslav Metelka
* 20 Gandalf 1.19 7/20/99 Miloslav Metelka
* 19 Gandalf 1.18 7/2/99 Miloslav Metelka
* 18 Gandalf 1.17 6/29/99 Miloslav Metelka Scrolling and patches
* 17 Gandalf 1.16 6/25/99 Miloslav Metelka from floats back to ints
* 16 Gandalf 1.15 6/8/99 Miloslav Metelka
* 15 Gandalf 1.14 6/1/99 Miloslav Metelka
* 14 Gandalf 1.13 5/15/99 Miloslav Metelka fixes
* 13 Gandalf 1.12 5/7/99 Miloslav Metelka line numbering and fixes
* 12 Gandalf 1.11 5/5/99 Miloslav Metelka
* 11 Gandalf 1.10 4/23/99 Miloslav Metelka Undo added and internal
* improvements
* 10 Gandalf 1.9 4/8/99 Miloslav Metelka
* 9 Gandalf 1.8 4/1/99 Miloslav Metelka
* 8 Gandalf 1.7 3/27/99 Miloslav Metelka
* 7 Gandalf 1.6 3/23/99 Miloslav Metelka
* 6 Gandalf 1.5 3/18/99 Miloslav Metelka
* 5 Gandalf 1.4 3/18/99 Miloslav Metelka
* 4 Gandalf 1.3 3/10/99 Jaroslav Tulach Hack to work with
* different document than BaseDocument
* 3 Gandalf 1.2 2/13/99 Miloslav Metelka
* 2 Gandalf 1.1 2/9/99 Miloslav Metelka
* 1 Gandalf 1.0 2/3/99 Miloslav Metelka
* $
*/